#ifndef __ENGINE_PACKAGE_QUEUE_included
#define __ENGINE_PACKAGE_QUEUE_included

#include <stdexcept>
#include <queue>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "package.h"

namespace Engine {

class CPackageQueue
{
	std::queue<boost::shared_ptr<CPackage>> mQueue;
	boost::mutex mMutex;
	boost::condition_variable_any mFullCond;
	boost::condition_variable_any mEmptyCond;   
    std::size_t const mMaxSize;
    bool mCloseFlag;

public :

	CPackageQueue(void) :
			mMaxSize(64),
			mCloseFlag(false){
	}

	~CPackageQueue(void) {
		Close();
	}

	void push(boost::shared_ptr<CPackage> p)
    {   
        boost::mutex::scoped_lock lock(mMutex);
        while (mQueue.size() >= mMaxSize && !mCloseFlag)
        {           
            mFullCond.wait(lock);
        }

        if (mCloseFlag)
        {
            throw std::runtime_error("PackageQueue::push : queue has been shutdown.");
        }

        mQueue.push(p);
        if(mQueue.size() == 1)
        {
            mEmptyCond.notify_all();   
        }
    }

	bool NonBlockPush(boost::shared_ptr<CPackage> p)
    {
        //    non-blocking push
        boost::mutex::scoped_lock lock(mMutex);
        if (mQueue.size() >= mMaxSize || mCloseFlag)
        {
            return false;
        }

        mQueue.push(p);
        if(mQueue.size() == 1)
        {
            mEmptyCond.notify_all();   
        }

        return true;
    }

	boost::shared_ptr<CPackage> pop()
    {
        boost::mutex::scoped_lock lock(mMutex);
        while (mQueue.empty() && !mCloseFlag)
        {           
            mEmptyCond.wait(lock);
        }

        if (mCloseFlag)
        {
            throw std::runtime_error("PackageQueue::pop : queue has been shutdown.");
        }

        boost::shared_ptr<CPackage> p = mQueue.front();
        mQueue.pop();
        if(mQueue.size() == mMaxSize - 1)
        {
            mFullCond.notify_all();   
        }
        return p;
    }

	boost::shared_ptr<CPackage> NonBlockPop()
    {
        //    non-blocking pop
        boost::mutex::scoped_lock lock(mMutex);
        if (mQueue.empty() || mCloseFlag)
        {           
            return boost::shared_ptr<CPackage>();
        }

        boost::shared_ptr<CPackage> p = mQueue.front();
        mQueue.pop();
        if(mQueue.size() == mMaxSize - 1)
        {
            mFullCond.notify_all();   
        }       
        return p;
    }

	void Close()
    {
        mCloseFlag = true;
        {
            boost::mutex::scoped_lock lock(mMutex);
            mFullCond.notify_all();   
            mEmptyCond.notify_all();
        }       
    }

    std::size_t getMaxSize()
    {
        return mMaxSize;
    }

    std::size_t getSize() // the result may be not newest due to other threads
    {
        boost::mutex::scoped_lock lock(mMutex);
        return mQueue.size();
    }
};

}

#endif //__ENGINE_PACKAGE_QUEUE_included